/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Engine2D.math;

/**
 *
 * @author Garys
 */
public class Matrix3d {

    /**
     * 
     */
    protected double[][] matrix = new double[3][3];

    /**
     * 
     * @param matrix
     */
    public Matrix3d(double[] matrix) {
        this.matrix[0][0] = matrix[0];
        this.matrix[0][1] = matrix[1];
        this.matrix[0][2] = matrix[2];
        this.matrix[1][0] = matrix[3];
        this.matrix[1][1] = matrix[4];
        this.matrix[1][2] = matrix[5];
        this.matrix[2][0] = matrix[6];
        this.matrix[2][1] = matrix[7];
        this.matrix[2][2] = matrix[8];
    }

    /**
     * 
     * @param x
     * @param y
     * @param z
     */
    public Matrix3d(Vector3d x, Vector3d y, Vector3d z) {
        this.matrix[0][0] = x.x;
        this.matrix[0][1] = x.y;
        this.matrix[0][2] = x.z;
        this.matrix[1][0] = y.x;
        this.matrix[1][1] = y.y;
        this.matrix[1][2] = y.z;
        this.matrix[2][0] = z.x;
        this.matrix[2][1] = z.y;
        this.matrix[2][2] = z.z;
    }

    /**
     * 
     * @param x
     * @param y
     * @param z
     */
    public Matrix3d(double[] x, double[] y, double[] z) {
        this.matrix[0][0] = x[0];
        this.matrix[0][1] = x[1];
        this.matrix[0][2] = x[2];
        this.matrix[1][0] = y[0];
        this.matrix[1][1] = y[1];
        this.matrix[1][2] = y[2];
        this.matrix[2][0] = z[0];
        this.matrix[2][1] = z[1];
        this.matrix[2][2] = z[2];
    }

    /**
     * 
     * @param xx
     * @param xy
     * @param xz
     * @param yx
     * @param yy
     * @param yz
     * @param zx
     * @param zy
     * @param zz
     */
    public Matrix3d(double xx, double xy, double xz, double yx, double yy, double yz, double zx, double zy, double zz) {
        this.matrix[0][0] = xx;
        this.matrix[0][1] = xy;
        this.matrix[0][2] = xz;
        this.matrix[1][0] = yx;
        this.matrix[1][1] = yy;
        this.matrix[1][2] = yz;
        this.matrix[2][0] = zx;
        this.matrix[2][1] = zy;
        this.matrix[2][2] = zz;
    }

    /**
     * 
     * @return
     */
    public Matrix3d negateMatrix3d() {
        return new Matrix3d(-matrix[0][0],
                -matrix[0][1],
                -matrix[0][2],
                -matrix[1][0],
                -matrix[1][1],
                -matrix[1][2],
                -matrix[2][0],
                -matrix[2][1],
                -matrix[2][2]);
    }

    /**
     * 
     * @param matrix3d
     * @return
     */
    public Matrix3d addMatrix3d(Matrix3d matrix3d) {
        return new Matrix3d(matrix[0][0] + matrix3d.matrix[0][0],
                matrix[0][1] + matrix3d.matrix[0][1],
                matrix[0][2] + matrix3d.matrix[0][2],
                matrix[1][0] + matrix3d.matrix[1][0],
                matrix[1][1] + matrix3d.matrix[1][1],
                matrix[1][2] + matrix3d.matrix[1][2],
                matrix[2][0] + matrix3d.matrix[2][0],
                matrix[2][1] + matrix3d.matrix[2][1],
                matrix[2][2] + matrix3d.matrix[2][2]);

    }

    /**
     * 
     * @param matrix3d
     */
    public void addMatrix3dToThis(Matrix3d matrix3d) {
        matrix[0][0] += matrix3d.matrix[0][0];
        matrix[0][1] += matrix3d.matrix[0][1];
        matrix[0][2] += matrix3d.matrix[0][2];
        matrix[1][0] += matrix3d.matrix[1][0];
        matrix[1][1] += matrix3d.matrix[1][1];
        matrix[1][2] += matrix3d.matrix[1][2];
        matrix[2][0] += matrix3d.matrix[2][0];
        matrix[2][1] += matrix3d.matrix[2][1];
        matrix[2][2] += matrix3d.matrix[2][2];
    }

    /**
     * 
     * @param matrix3d
     * @return
     */
    public Matrix3d subtractMatrix3d(Matrix3d matrix3d) {

        return new Matrix3d(matrix[0][0] - matrix3d.matrix[0][0],
                matrix[0][1] - matrix3d.matrix[0][1],
                matrix[0][2] - matrix3d.matrix[0][2],
                matrix[1][0] - matrix3d.matrix[1][0],
                matrix[1][1] - matrix3d.matrix[1][1],
                matrix[1][2] - matrix3d.matrix[1][2],
                matrix[2][0] - matrix3d.matrix[2][0],
                matrix[2][1] - matrix3d.matrix[2][1],
                matrix[2][2] - matrix3d.matrix[2][2]);

    }

    /**
     * 
     * @param matrix3d
     */
    public void subtractMatrix3dToThis(Matrix3d matrix3d) {
        matrix[0][0] -= matrix3d.matrix[0][0];
        matrix[0][1] -= matrix3d.matrix[0][1];
        matrix[0][2] -= matrix3d.matrix[0][2];
        matrix[1][0] -= matrix3d.matrix[1][0];
        matrix[1][1] -= matrix3d.matrix[1][1];
        matrix[1][2] -= matrix3d.matrix[1][2];
        matrix[2][0] -= matrix3d.matrix[2][0];
        matrix[2][1] -= matrix3d.matrix[2][1];
        matrix[2][2] -= matrix3d.matrix[2][2];
    }

    /**
     * 
     * @param scale
     * @return
     */
    public Matrix3d scaleMatrix3d(double scale) {

        return new Matrix3d(matrix[0][0] * scale,
                matrix[0][1] * scale,
                matrix[0][2] * scale,
                matrix[1][0] * scale,
                matrix[1][1] * scale,
                matrix[1][2] * scale,
                matrix[2][0] * scale,
                matrix[2][1] * scale,
                matrix[2][2] * scale);

    }

    /**
     * 
     * @param scale
     */
    public void scaleMatrix3dToThis(double scale) {
        matrix[0][0] *= scale;
        matrix[0][1] *= scale;
        matrix[0][2] *= scale;
        matrix[1][0] *= scale;
        matrix[1][1] *= scale;
        matrix[1][2] *= scale;
        matrix[2][0] *= scale;
        matrix[2][1] *= scale;
        matrix[2][2] *= scale;
    }

    /**
     * 
     * @param matrix3d
     * @return
     */
    public Matrix3d multiplyMatrix3d(Matrix3d matrix3d) {

        return new Matrix3d(matrix[0][0] * matrix3d.matrix[0][0],
                matrix[0][1] * matrix3d.matrix[0][1],
                matrix[0][2] * matrix3d.matrix[0][2],
                matrix[1][0] * matrix3d.matrix[1][0],
                matrix[1][1] * matrix3d.matrix[1][1],
                matrix[1][2] * matrix3d.matrix[1][2],
                matrix[2][0] * matrix3d.matrix[2][0],
                matrix[2][1] * matrix3d.matrix[2][1],
                matrix[2][2] * matrix3d.matrix[2][2]);

    }

    /**
     * 
     * @param matrix3d
     */
    public void multiplyMatrix3dToThis(Matrix3d matrix3d) {

        matrix[0][0] *= matrix3d.matrix[0][0];
        matrix[0][1] *= matrix3d.matrix[0][1];
        matrix[0][2] *= matrix3d.matrix[0][2];
        matrix[1][0] *= matrix3d.matrix[1][0];
        matrix[1][1] *= matrix3d.matrix[1][1];
        matrix[1][2] *= matrix3d.matrix[1][2];
        matrix[2][0] *= matrix3d.matrix[2][0];
        matrix[2][1] *= matrix3d.matrix[2][1];
        matrix[2][2] *= matrix3d.matrix[2][2];

    }

    /**
     * 
     * @param row
     * @param vector3d
     */
    public void setRow(int row, Vector3d vector3d) {
        matrix[row][0] = vector3d.x;
        matrix[row][1] = vector3d.y;
        matrix[row][2] = vector3d.z;

    }

    /**
     * 
     * @param column
     * @param vector3d
     */
    public void setColumn(int column, Vector3d vector3d) {
        matrix[0][column] = vector3d.x;
        matrix[1][column] = vector3d.y;
        matrix[2][column] = vector3d.z;
    }

    /**
     * 
     * @param row
     * @return
     */
    public Vector3d getRow(int row) {
        return new Vector3d(matrix[row][0], matrix[row][1], matrix[row][2]);
    }

    /**
     * 
     * @param column
     * @return
     */
    public Vector3d getColumn(int column) {
        return new Vector3d(matrix[0][column], matrix[1][column], matrix[2][column]);
    }

    /**
     * 
     */
    public void setIdentity() {
        matrix[0][0] = 1d;
        matrix[0][1] = 0d;
        matrix[0][2] = 0d;
        matrix[1][0] = 0d;
        matrix[1][1] = 1d;
        matrix[1][2] = 0d;
        matrix[2][0] = 0d;
        matrix[2][1] = 0d;
        matrix[2][2] = 1d;
    }

    /**
     * 
     */
    public void setZero() {
        matrix[0][0] = 0d;
        matrix[0][1] = 0d;
        matrix[0][2] = 0d;
        matrix[1][0] = 0d;
        matrix[1][1] = 0d;
        matrix[1][2] = 0d;
        matrix[2][0] = 0d;
        matrix[2][1] = 0d;
        matrix[2][2] = 0d;
    }

    /**
     * 
     */
    public void transpose() {
        double i = matrix[0][1];
        matrix[0][1] = matrix[1][0];
        matrix[1][0] = i;

        i = matrix[0][2];
        matrix[0][2] = matrix[2][0];
        matrix[2][0] = i;

        i = matrix[1][2];
        matrix[1][2] = matrix[2][1];
        matrix[2][1] = i;
    }

    /**
     * 
     * @param Matrix3d
     */
    public void getTranspose(Matrix3d Matrix3d) {

        Matrix3d.matrix[0][0] = matrix[0][0];
        Matrix3d.matrix[0][1] = matrix[1][0];
        Matrix3d.matrix[0][2] = matrix[2][1];

        Matrix3d.matrix[1][0] = matrix[0][1];
        Matrix3d.matrix[1][1] = matrix[1][1];
        Matrix3d.matrix[1][2] = matrix[2][1];

        Matrix3d.matrix[2][0] = matrix[0][2];
        Matrix3d.matrix[2][1] = matrix[1][2];
        Matrix3d.matrix[2][2] = matrix[2][2];

    }

    /**
     * 
     * @return
     */
    public double determinate() {
        return matrix[0][0] * (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1]) + matrix[0][1] * (matrix[1][2] * matrix[2][0] - matrix[1][0] * matrix[2][2]) + matrix[0][2] * (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]);
    }

    /**
     * 
     * @param vector3d
     */
    public void scale(Vector3d vector3d) {
        matrix[0][0] = vector3d.x;
        matrix[0][1] = 0d;
        matrix[0][2] = 0d;
        matrix[1][0] = 0d;
        matrix[1][1] = vector3d.y;
        matrix[1][2] = 0d;
        matrix[2][0] = 0d;
        matrix[2][1] = 0d;
        matrix[2][2] = vector3d.z;


    }

    /**
     * 
     * @param x
     * @param y
     * @param z
     */
    public void scale(double x, double y, double z) {
        matrix[0][0] = x;
        matrix[0][1] = 0d;
        matrix[0][2] = 0d;
        matrix[1][0] = 0d;
        matrix[1][1] = y;
        matrix[1][2] = 0d;
        matrix[2][0] = 0d;
        matrix[2][1] = 0d;
        matrix[2][2] = z;
    }

    /**
     * 
     * @param scale
     */
    public void scale(double scale) {

        scale(scale, scale, scale);

    }

    /**
     * 
     * @param angle
     */
    public void rotateX(double angle) {

        double cosAngle = (double) Math.cos(angle);
        double sinAngle = (double) Math.sin(angle);
        matrix[0][0] = 1d;
        matrix[0][1] = 0d;
        matrix[0][2] = 0d;
        matrix[1][0] = 0d;
        matrix[1][1] = cosAngle;
        matrix[1][2] = sinAngle;
        matrix[2][0] = 0d;
        matrix[2][1] = -sinAngle;
        matrix[2][2] = cosAngle;

    }

    /**
     * 
     * @param angle
     */
    public void rotateY(double angle) {
        double cosAngle = (double) Math.cos(angle);
        double sinAngle = (double) Math.sin(angle);
        matrix[0][0] = cosAngle;
        matrix[0][1] = 0d;
        matrix[0][2] = -sinAngle;
        matrix[1][0] = 0d;
        matrix[1][1] = 1d;
        matrix[1][2] = 0d;
        matrix[2][0] = sinAngle;
        matrix[2][1] = 0d;
        matrix[2][2] = cosAngle;
    }
    
    /**
     * 
     * @param angle
     */
    public void rotateZ(double angle) {
        double cosAngle = (double) Math.cos(angle);
        double sinAngle = (double) Math.sin(angle);
        matrix[0][0] = cosAngle;
        matrix[0][1] = sinAngle;
        matrix[0][2] = 0d;
        matrix[1][0] = -sinAngle;
        matrix[1][1] = cosAngle;
        matrix[1][2] = 0d;
        matrix[2][0] = 0d;
        matrix[2][1] = 0d;
        matrix[2][2] = 1d;
    }
    

    @Override
    public String toString() {
        return "Matrix3d{" + "matrix=" + matrix[0][0] + " "
                + matrix[0][1] + " "
                + matrix[0][2] + " "
                + matrix[1][0] + " "
                + matrix[1][1] + " "
                + matrix[1][2] + " "
                + matrix[2][0] + " "
                + matrix[2][1] + " "
                + matrix[2][2];
    }
    
}
